#include "StdAfx.h"
#include "HXModel.h"
#include "objLoader.h"

#include <limits>
#include <fstream>
#include <gdiplus.h>

//-----------------------------------------------------------------------------

void HXImageRGBA::CopyFrom(void* m_pOtherData, size_t width, size_t height)
{ 
	delete [] m_pData;
	m_pData = new HXRGBA[width*height];
	memcpy(m_pData, m_pOtherData, width*height*sizeof(HXRGBA));
	m_width = width;
	m_height = height;
}

void HXImageRGBA::AdjustAlpha(float alpha_scale)
{ 
	if (alpha_scale != 1.0f)
	{
		for (size_t i = 0; i < GetSize(); i++)
		{
			if (alpha_scale <= 0.0f)
			{
				m_pData[i].component[3] = 255;
			}
			else
			{
				int s = static_cast<int>(m_pData[i].component[3]/alpha_scale);
				if (s > 255)
					m_pData[i].component[3] = 255;
				else
					m_pData[i].component[3] = static_cast<unsigned char>(s);
			}
		}
	}
}

void HXImageRGBA::MorphWith(void* m_pOtherData, float blend_frac, size_t otherWidth, size_t otherHeight)
{ 
	HXRGBA* otherData = reinterpret_cast<HXRGBA*>(m_pOtherData);
	if ((GetWidth() == otherWidth) && (GetHeight() == otherHeight))
	{
		for (size_t i = 0; i < GetSize(); i++)
		{
			for (size_t c = 0; c < 3 /* no alpha morphing */; c++)
			{
				m_pData[i].component[c] = static_cast<unsigned char>(blend_frac*otherData[i].component[c] + (1.0f-blend_frac)*m_pData[i].component[c]);
			}
		}
	}
	else // try to map from the new data to the existing image
	if ((otherWidth > 0) && (otherHeight > 0))
	{
		for (size_t h = 0; h < GetHeight(); h++)
		{
			for (size_t w = 0; w < GetWidth(); w++)
			{
				size_t myIndex = (h * GetWidth()) + w;
				size_t otherIndex = (((h * otherHeight) / GetHeight()) * otherWidth) + ((w * otherWidth) / GetWidth());
				for (size_t c = 0; c < 3 /* no alpha morphing */; c++)
				{
					m_pData[myIndex].component[c] = static_cast<unsigned char>(blend_frac*otherData[otherIndex].component[c] + (1.0f-blend_frac)*m_pData[myIndex].component[c]);
				}
			}
		}
	}
}	

void HXImageRGBA::FlipHorizontal()
{
	vector<HXRGBA> flippedLine(GetWidth());
	for (size_t yy = 0; yy < GetHeight()/2; yy++)
	{
		for (size_t xx = 0; xx < GetWidth(); xx++)
		{
			flippedLine[xx] = m_pData[yy * GetWidth() + xx];
		}
		for (size_t xx = 0; xx < GetWidth(); xx++)
		{
			m_pData[yy * GetWidth() + xx] = m_pData[(GetHeight() - 1 - yy) * GetWidth() + xx];
		}
		for (size_t xx = 0; xx < GetWidth(); xx++)
		{
			m_pData[(GetHeight() - 1 - yy) * GetWidth() + xx] = flippedLine[xx];
		}
	}
}

//! Load a bitmap image file.
bool HXImageRGBA::LoadFromFile(const wstring &fname, float alpha_scale)
{
	using namespace Gdiplus;
	bool success = false;
	delete [] m_pData; m_pData = NULL;
	ULONG_PTR m_gdipInstanceHandle;
    GdiplusStartupInput m_gdipStartupInput;
	GdiplusStartup(&m_gdipInstanceHandle, &m_gdipStartupInput, NULL);
	{
		Bitmap bmp(fname.c_str());
		if (bmp.GetWidth() * bmp.GetHeight() > 0)
		{
			Rect rect;
			rect.X = 0; 
			rect.Y = 0; 
			rect.Width = bmp.GetWidth();
			rect.Height = bmp.GetHeight();
			BitmapData data;
			bmp.LockBits(&rect,ImageLockModeRead,PixelFormat32bppARGB,&data);
			if ((data.Stride % 4) == 0)
			{
				Resize(data.Width,data.Height);
				HXRGBA* sptr = reinterpret_cast<HXRGBA*>(data.Scan0);
				HXRGBA* dptr = m_pData;
				size_t sstep = data.Stride / 4;
				for (size_t yy = 0; yy < data.Height; yy++)
				{
					for (size_t xx = 0; xx < data.Width; xx++)
					{
						dptr[xx].component[0] = sptr[xx].component[2];
						dptr[xx].component[1] = sptr[xx].component[1];
						dptr[xx].component[2] = sptr[xx].component[0];
						if (alpha_scale != 1.0f)
						{
							if (alpha_scale <= 0.0f)
							{
								dptr[xx].component[3] = 255;
							}
							else
							{
								int s = static_cast<int>(sptr[xx].component[3]/alpha_scale);
								if (s > 255)
									dptr[xx].component[3] = 255;
								else
									dptr[xx].component[3] = static_cast<unsigned char>(s);
							}
						}
						else
						{
							dptr[xx].component[3] = sptr[xx].component[3];
						}
					}
					dptr += GetWidth();
					sptr += sstep;
				}
				bmp.UnlockBits(&data);    
				success = true;
			}
		}
	}
	GdiplusShutdown(m_gdipInstanceHandle);
	return success;
}

void HXImageRGBA::Resize(size_t width, size_t height)
{
	delete [] m_pData;
	m_pData = new HXRGBA[width*height];
	m_height = height;
	m_width = width;
}

void HXImageRGBA::FromBinaryStream(istream& in)
{
	in.read(reinterpret_cast<char *>(&m_height), sizeof(size_t));
	in.read(reinterpret_cast<char *>(&m_width), sizeof(size_t));
	delete [] m_pData;
	m_pData = new HXRGBA[GetSize()];
	in.read(reinterpret_cast<char *>(m_pData), GetDataSize());
}

void HXImageRGBA::ToBinaryStream(ostream& out)
{
	out.write(reinterpret_cast<char *>(&m_height), sizeof(size_t));
	out.write(reinterpret_cast<char *>(&m_width), sizeof(size_t));
	out.write(reinterpret_cast<char *>(m_pData), GetDataSize());
}

//-----------------------------------------------------------------------------

void HXMaterial::FromBinaryStream(istream& in)
{
	texture.FromBinaryStream(in);
	ambient.FromBinaryStream(in);
	diffuse.FromBinaryStream(in);
	specular.FromBinaryStream(in);
	in.read(reinterpret_cast<char *>(&shininess), sizeof(float));
}

void HXMaterial::ToBinaryStream(ostream& out)
{
	texture.ToBinaryStream(out);
	ambient.ToBinaryStream(out);
	diffuse.ToBinaryStream(out);
	specular.ToBinaryStream(out);
	out.write(reinterpret_cast<char *>(&shininess), sizeof(float));
}

//-----------------------------------------------------------------------------

bool HXModel::ImportFromObjFile(const wstring& fname)
{
	bool success = true;
	objLoader *objData = new objLoader();
	if (objData->load(WStringToString(fname).c_str()))
	{
		// import vertices
		vertexList.resize(objData->vertexCount);
		for (size_t i = 0; i < vertexList.size(); i++)
		{
			vertexList[i][0] = static_cast<float>(objData->vertexList[i]->e[0]);
			vertexList[i][1] = static_cast<float>(objData->vertexList[i]->e[1]);
			vertexList[i][2] = static_cast<float>(objData->vertexList[i]->e[2]);
		}
		// import texture coords
		textureList.resize(objData->textureCount);
		for (size_t i = 0; i < textureList.size(); i++)
		{
			textureList[i][0] = static_cast<float>(objData->textureList[i]->e[0]);
			textureList[i][1] = static_cast<float>(objData->textureList[i]->e[1]);
		}
		// count faces
		size_t numObjTriangles = 0;
		size_t numObjQuads = 0;
		for (int i = 0; i < objData->faceCount; i++)
		{
			switch (objData->faceList[i]->vertex_count)
			{
			case 3:
				numObjTriangles++;
				break;
			case 4:
				numObjQuads++;
				break;
			default:
				break;
			}
		}
		// import faces
		triangleList.resize(numObjTriangles);
		textureTriangleList.resize(numObjTriangles);
		materialTriangleList.resize(numObjTriangles);
		quadList.resize(numObjQuads);
		textureQuadList.resize(numObjQuads);
		materialQuadList.resize(numObjQuads);
		numObjTriangles = 0;
		numObjQuads = 0;
		for (int i = 0; i < objData->faceCount; i++)
		{
			switch (objData->faceList[i]->vertex_count)
			{
			case 3:
				triangleList[numObjTriangles][0] = objData->faceList[i]->vertex_index[0];
				triangleList[numObjTriangles][1] = objData->faceList[i]->vertex_index[1];
				triangleList[numObjTriangles][2] = objData->faceList[i]->vertex_index[2];
				textureTriangleList[numObjTriangles][0] = objData->faceList[i]->texture_index[0];
				textureTriangleList[numObjTriangles][1] = objData->faceList[i]->texture_index[1];
				textureTriangleList[numObjTriangles][2] = objData->faceList[i]->texture_index[2];
				materialTriangleList[numObjTriangles] = objData->faceList[i]->material_index;
				numObjTriangles++;
				break;
			case 4:
				quadList[numObjQuads][0] = objData->faceList[i]->vertex_index[0];
				quadList[numObjQuads][1] = objData->faceList[i]->vertex_index[1];
				quadList[numObjQuads][2] = objData->faceList[i]->vertex_index[2];
				quadList[numObjQuads][3] = objData->faceList[i]->vertex_index[3];
				textureQuadList[numObjQuads][0] = objData->faceList[i]->texture_index[0];
				textureQuadList[numObjQuads][1] = objData->faceList[i]->texture_index[1];
				textureQuadList[numObjQuads][2] = objData->faceList[i]->texture_index[2];
				textureQuadList[numObjQuads][3] = objData->faceList[i]->texture_index[3];
				materialQuadList[numObjQuads] = objData->faceList[i]->material_index;
				numObjQuads++;
				break;
			default:
				break;
			}
		}
		// load materials
		materialList.resize(objData->materialCount);
		textureCount = 0;
		for (int i = 0; i < objData->materialCount; i++)
		{
			wstring texFname = StringToWString(string(objData->materialList[i]->texture_filename));
			if (texFname.size())
			{
				bool texLoaded = materialList[i].texture.LoadFromFile(texFname);
				if (!texLoaded)
				{
					// try again with the full path
					wchar_t filePath[MAX_PATH];
					wchar_t fullTex[MAX_PATH];
					wcscpy_s(filePath, MAX_PATH, fname.c_str());
					PathRemoveFileSpec(filePath);
					swprintf_s(fullTex, L"%s\\%s", filePath, texFname.c_str());
					texLoaded = materialList[i].texture.LoadFromFile(fullTex);
				}
				if (texLoaded)
				{
					materialList[i].texture.FlipHorizontal();
					textureCount++;
				}
				else
				{
					success &= false;
				}
			}
			for (size_t j = 0; j < 3; j++)
			{
				materialList[i].ambient[j] = static_cast<float>(objData->materialList[i]->amb[j]);
				materialList[i].diffuse[j] = static_cast<float>(objData->materialList[i]->diff[j]);
				materialList[i].specular[j] = static_cast<float>(objData->materialList[i]->spec[j]);
			}
			materialList[i].shininess = static_cast<float>(objData->materialList[i]->shiny);
		}
	}
	else
	{
		success &= false;
	}
	delete objData;
	if (success) Finalize(true);
	return success;
}

bool HXModel::ImportObjBlendShape(const wstring& wlabel, const wstring& fname)
{
	objLoader *objData = new objLoader();
	if (wlabel.size() && objData->load(WStringToString(fname).c_str()))
	{
		size_t originalVertexSize = vertexList.size() - vertexAdded.size();
		if (static_cast<size_t>(objData->vertexCount) == originalVertexSize)
		{
			diffMorphList.push_back(HXDiffMorph());
			diffMorphList.back().wlabel = wlabel;
			diffMorphList.back().vertexDeltaList.resize(vertexList.size());
			// import vertices
			size_t i = 0;
			for (; i < originalVertexSize; i++)
			{
				diffMorphList.back().vertexDeltaList[i][0] = static_cast<float>(objData->vertexList[i]->e[0]) - vertexList[i][0];
				diffMorphList.back().vertexDeltaList[i][1] = static_cast<float>(objData->vertexList[i]->e[1]) - vertexList[i][1];
				diffMorphList.back().vertexDeltaList[i][2] = static_cast<float>(objData->vertexList[i]->e[2]) - vertexList[i][2];
			}
			// handle vertices added through Finalize()
			for (size_t j = 0; j < vertexAdded.size(); i++, j++)
			{
				diffMorphList.back().vertexDeltaList[i][0] = static_cast<float>(objData->vertexList[vertexAdded[j]]->e[0]) - vertexList[vertexAdded[j]][0];
				diffMorphList.back().vertexDeltaList[i][1] = static_cast<float>(objData->vertexList[vertexAdded[j]]->e[1]) - vertexList[vertexAdded[j]][1];
				diffMorphList.back().vertexDeltaList[i][2] = static_cast<float>(objData->vertexList[vertexAdded[j]]->e[2]) - vertexList[vertexAdded[j]][2];
			}
			delete objData;
			return true;
		}
	}
	delete objData;
	return false;
}

bool HXModel::IsFileAModel(const wstring& fname)
{
	 char version[64];
     fstream binary_file(fname.c_str(), ios::binary|ios::in);
	 binary_file.read(version, strlen(HXMODEL_ID));
	 binary_file.close();
	 return !strcmp(version, HXMODEL_ID);
}

bool HXModel::LoadFromFile(const wstring& fname, size_t organIndex)
{
	 char version[64];
	 size_t curIndex = organIndex;
     fstream binary_file(fname.c_str(), ios::binary|ios::in);
	 if (binary_file.good())
	 {
		size_t idLength = strlen(HXMODEL_ID);
		binary_file.read(version, idLength);
		version[idLength] = '\0';
		if (!strcmp(version, HXMODEL_ID)) 
		{
			HXModelHeader hdr;
			while (true)
			{
				binary_file.read(reinterpret_cast<char *>(&hdr),sizeof(HXModelHeader));
				alphaThreshold = hdr.alphaThreshold;
				textureMode = hdr.textureMode;
				if (curIndex == 0)
				{
					vertexList.resize(hdr.vertexCount);
					for (size_t i = 0; i < hdr.vertexCount; i++) vertexList[i].FromBinaryStream(binary_file);
					statVertexList.resize(hdr.statMorphVertexCount);
					for (size_t i = 0; i < hdr.statMorphVertexCount; i++) statVertexList[i].FromBinaryStream(binary_file);
					vertexAdded.resize(hdr.vertexAdded);
					for (size_t i = 0; i < hdr.vertexAdded; i++) binary_file.read(reinterpret_cast<char *>(&(vertexAdded[i])), sizeof(int));
					triangleList.resize(hdr.triangleCount);
					for (size_t i = 0; i < hdr.triangleCount; i++) triangleList[i].FromBinaryStream(binary_file);
					quadList.resize(hdr.quadCount);
					for (size_t i = 0; i < hdr.quadCount; i++) quadList[i].FromBinaryStream(binary_file);
					diffMorphList.resize(hdr.diffMorphCount);
					for (size_t i = 0; i < hdr.diffMorphCount; i++) diffMorphList[i].FromBinaryStream(binary_file);
					statMorphList.resize(hdr.statMorphCount);
					for (size_t i = 0; i < hdr.statMorphCount; i++) statMorphList[i].FromBinaryStream(binary_file);
					textureCount = 0;
					materialList.resize(hdr.materialCount);
					for (size_t i = 0; i < hdr.materialCount; i++) 
					{
						materialList[i].FromBinaryStream(binary_file);
						if (!materialList[i].texture.IsEmpty())
						{
							textureCount++;
						}
					}
					materialTriangleList.resize(hdr.materialTriangleCount);
					for (size_t i = 0; i < hdr.materialTriangleCount; i++) binary_file.read(reinterpret_cast<char *>(&(materialTriangleList[i])), sizeof(int));
					materialQuadList.resize(hdr.materialQuadCount);
					for (size_t i = 0; i < hdr.materialQuadCount; i++) binary_file.read(reinterpret_cast<char *>(&(materialQuadList[i])), sizeof(int));
					textureList.resize(hdr.texturePointCount);
					for (size_t i = 0; i < hdr.texturePointCount; i++) textureList[i].FromBinaryStream(binary_file);
					expandedTextureList.resize(hdr.texturePointCountEx);
					for (size_t i = 0; i < hdr.texturePointCountEx; i++) expandedTextureList[i].FromBinaryStream(binary_file);
					textureTriangleList.resize(hdr.triangleCount);
					for (size_t i = 0; i < hdr.triangleCount; i++) textureTriangleList[i].FromBinaryStream(binary_file);
					textureQuadList.resize(hdr.quadCount);
					for (size_t i = 0; i < hdr.quadCount; i++) textureQuadList[i].FromBinaryStream(binary_file);
					binary_file.close();
					Finalize(false);
					return true;
				}
				else
				{
					curIndex--;
				}
				if (hdr.nextModel >= binary_file.tellg())
				{
					binary_file.seekg(hdr.nextModel);
				}
				else
				{
					binary_file.close();
					return false;
				}
			} 
		}
	 }
	 binary_file.close();
	 return false;
}

bool HXModel::SaveToFile(const wstring& fname)
{
	fstream binary_file(fname.c_str(), ios::out|ios::binary|ios::app);
	if (binary_file.good())
	{
		binary_file.write(HXMODEL_ID, strlen(HXMODEL_ID));
		SaveToFileStream(binary_file, false);
		binary_file.close();
		return true;
	}
	binary_file.close();
    return false;
}

bool HXModel::SaveToFile(const wstring& fname, list<HXModel*>& modelList)
{
	fstream binary_file(fname.c_str(), ios::out|ios::binary|ios::trunc);
	if (binary_file.good())
	{
		binary_file.write(HXMODEL_ID, strlen(HXMODEL_ID));
		list<HXModel*>::iterator mit = modelList.begin();
		for (size_t i = 0; mit != modelList.end(); ++mit, i++)
		{
			(*mit)->SaveToFileStream(binary_file, (i >= (modelList.size() - 1)) ? false : true);
		}
		binary_file.close();
		return true;
	}
	binary_file.close();
    return false;
}

void HXModel::SaveToFileStream(fstream& fstr, bool bSubsequentModel)
{
	HXModelHeader hdr;
	hdr.nextModel = fstr.tellp();
	hdr.alphaThreshold = alphaThreshold;
	hdr.textureMode = textureMode;
	hdr.vertexCount = modelVertexSize;
	hdr.vertexAdded = static_cast<unsigned long>(vertexAdded.size());
	hdr.triangleCount = static_cast<unsigned long>(triangleList.size());
	hdr.quadCount = static_cast<unsigned long>(quadList.size());
	hdr.diffMorphCount = static_cast<unsigned long>(diffMorphList.size());
	hdr.statMorphCount = static_cast<unsigned long>(statMorphList.size());
	hdr.statMorphVertexCount = static_cast<unsigned long>(statVertexList.size());
	hdr.materialCount = static_cast<unsigned long>(materialList.size());
	hdr.materialTriangleCount = static_cast<unsigned long>(materialTriangleList.size());
	hdr.materialQuadCount = static_cast<unsigned long>(materialQuadList.size());
	hdr.texturePointCount = static_cast<unsigned long>(textureList.size());
	hdr.texturePointCountEx = static_cast<unsigned long>(expandedTextureList.size());

	fstr.write(reinterpret_cast<char *>(&hdr), sizeof(HXModelHeader));
	for (size_t i = 0; i < hdr.vertexCount; i++) vertexList[i].ToBinaryStream(fstr);
	for (size_t i = 0; i < hdr.statMorphVertexCount; i++) statVertexList[i].ToBinaryStream(fstr);
	for (size_t i = 0; i < hdr.vertexAdded; i++) fstr.write(reinterpret_cast<char *>(&(vertexAdded[i])), sizeof(int));
	for (size_t i = 0; i < hdr.triangleCount; i++) triangleList[i].ToBinaryStream(fstr);
	for (size_t i = 0; i < hdr.quadCount; i++) quadList[i].ToBinaryStream(fstr);
	for (size_t i = 0; i < hdr.diffMorphCount; i++) diffMorphList[i].ToBinaryStream(fstr);
	for (size_t i = 0; i < hdr.statMorphCount; i++) statMorphList[i].ToBinaryStream(fstr);
	for (size_t i = 0; i < hdr.materialCount; i++) materialList[i].ToBinaryStream(fstr);
	for (size_t i = 0; i < hdr.materialTriangleCount; i++) fstr.write(reinterpret_cast<char *>(&(materialTriangleList[i])), sizeof(int));
	for (size_t i = 0; i < hdr.materialQuadCount; i++) fstr.write(reinterpret_cast<char *>(&(materialQuadList[i])), sizeof(int));
	for (size_t i = 0; i < hdr.texturePointCount; i++) textureList[i].ToBinaryStream(fstr);
	for (size_t i = 0; i < hdr.texturePointCountEx; i++) expandedTextureList[i].ToBinaryStream(fstr);
	for (size_t i = 0; i < hdr.triangleCount; i++) textureTriangleList[i].ToBinaryStream(fstr);
	for (size_t i = 0; i < hdr.quadCount; i++) textureQuadList[i].ToBinaryStream(fstr);

	if (bSubsequentModel)
	{
		fstr.flush();
		streampos endpos = fstr.tellp();
		fstr.seekp(hdr.nextModel);
		hdr.nextModel = endpos;
		fstr.write(reinterpret_cast<char *>(&hdr), sizeof(HXModelHeader));
		fstr.seekp(endpos);
	}
	fstr.flush();
}

void HXModel::Finalize(bool adjustTextures)
{
	//vector<HXVector3F> statVertexList;
	modelVertexSize = vertexList.size();
	
	if (adjustTextures) 
	{

		if (statMorphList.size())
		{
			// determine actual number of vertices in model (without stat morphs):
			for (size_t i = 0; i < statMorphList.size(); i++)
			{
				if (statMorphList[i].firstVertexOffset < modelVertexSize)
				{
					modelVertexSize = statMorphList[i].firstVertexOffset;
				}
			}
			size_t v = modelVertexSize;
			statVertexList.reserve(vertexList.size()-modelVertexSize);
			while (v < vertexList.size())
			{
				statVertexList.push_back(vertexList[v++]);
			}
			vertexList.resize(modelVertexSize);
		}

		if (textureList.size() > 0)
		{	
			// ensure there's one texture coordinate for each vertex and set texture mode accordingly:
			if (textureTriangleList.size() == triangleList.size() && textureQuadList.size() == quadList.size()) // per facet texture
			{
				textureMode = HXTM_PERFACET_SINGLECOORD;
				expandedTextureList.resize(vertexList.size(), HXVector2F(numeric_limits<float>::infinity(), 0)); // include stat morphs??
				for (size_t i = 0; i < triangleList.size(); i++)
				{
					if ((expandedTextureList[triangleList[i][0]][0] != numeric_limits<float>::infinity() && !(expandedTextureList[triangleList[i][0]] == textureList[textureTriangleList[i][0]]))
						|| (expandedTextureList[triangleList[i][1]][0] != numeric_limits<float>::infinity() && !(expandedTextureList[triangleList[i][1]] == textureList[textureTriangleList[i][1]]))
						|| (expandedTextureList[triangleList[i][2]][0] != numeric_limits<float>::infinity() && !(expandedTextureList[triangleList[i][2]] == textureList[textureTriangleList[i][2]]))
						)
					{
						vertexList.push_back(vertexList[triangleList[i][0]]);
						vertexList.push_back(vertexList[triangleList[i][1]]);
						vertexList.push_back(vertexList[triangleList[i][2]]);
						vertexAdded.push_back(triangleList[i][0]);
						vertexAdded.push_back(triangleList[i][1]);
						vertexAdded.push_back(triangleList[i][2]);
						triangleList[i] = HXVectorTriangle(vertexList.size()-3, vertexList.size()-2, vertexList.size()-1);
						expandedTextureList.resize(vertexList.size(), HXVector2F(numeric_limits<float>::infinity(), 0));
					}
					expandedTextureList[triangleList[i][0]] = textureList[textureTriangleList[i][0]];
					expandedTextureList[triangleList[i][1]] = textureList[textureTriangleList[i][1]];
					expandedTextureList[triangleList[i][2]] = textureList[textureTriangleList[i][2]];
				}

				for (size_t i = 0; i < quadList.size(); i++)
				{
				
					if ((expandedTextureList[quadList[i][0]][0] != numeric_limits<float>::infinity() && !(expandedTextureList[quadList[i][0]] == textureList[textureQuadList[i][0]]))
						|| (expandedTextureList[quadList[i][1]][0] != numeric_limits<float>::infinity() && !(expandedTextureList[quadList[i][1]] == textureList[textureQuadList[i][1]]))
						|| (expandedTextureList[quadList[i][2]][0] != numeric_limits<float>::infinity() && !(expandedTextureList[quadList[i][2]] == textureList[textureQuadList[i][2]]))
						|| (expandedTextureList[quadList[i][3]][0] != numeric_limits<float>::infinity() && !(expandedTextureList[quadList[i][3]] == textureList[textureQuadList[i][3]]))
						)
					{
						vertexList.push_back(vertexList[quadList[i][0]]);
						vertexList.push_back(vertexList[quadList[i][1]]);
						vertexList.push_back(vertexList[quadList[i][2]]);
						vertexList.push_back(vertexList[quadList[i][3]]);
						vertexAdded.push_back(quadList[i][0]);
						vertexAdded.push_back(quadList[i][1]);
						vertexAdded.push_back(quadList[i][2]);
						vertexAdded.push_back(quadList[i][3]);
						quadList[i] = HXVectorQuad(vertexList.size()-4, vertexList.size()-3, vertexList.size()-2, vertexList.size()-1);
						expandedTextureList.resize(vertexList.size(), HXVector2F(numeric_limits<float>::infinity(), 0));
					}
					expandedTextureList[quadList[i][0]] = textureList[textureQuadList[i][0]];
					expandedTextureList[quadList[i][1]] = textureList[textureQuadList[i][1]];
					expandedTextureList[quadList[i][2]] = textureList[textureQuadList[i][2]];
					expandedTextureList[quadList[i][3]] = textureList[textureQuadList[i][3]];
				}
				modelVertexSize += vertexAdded.size();
			}
			else if (modelVertexSize <= textureList.size()) // per vertex texture
			{
				textureMode = HXTM_PERVERTEX;
			}
			else // data is incomplete
			{
				textureMode = HXTM_INVALID;
			}
		}
		else // no texture
		{
			textureMode = HXTM_NOTEXTURE;
		}

		if (statVertexList.size())
		{
			vertexList.reserve(modelVertexSize+statVertexList.size());
			size_t s = 0;
			while (s < statVertexList.size())
			{
				vertexList.push_back(statVertexList[s++]);
			}
			for (size_t i = 0; i < statMorphList.size(); i++)
			{
				statMorphList[i].firstVertexOffset += vertexAdded.size();
			}
		}

		for (size_t i = 0; i < diffMorphList.size(); i++)
		{
			size_t originalVertexSize = diffMorphList[i].vertexDeltaList.size();
			diffMorphList[i].vertexDeltaList.reserve(originalVertexSize+vertexAdded.size());
			// handle vertices added through Finalize()
			for (size_t j = 0; j < vertexAdded.size(); j++)
			{
				diffMorphList[i].vertexDeltaList.push_back(diffMorphList[i].vertexDeltaList[vertexAdded[j]]);
			}
		}
	} 
	/*else 
	{
		switch (textureMode) 
		{
			case HXTM_PERFACET_SINGLECOORD:
				modelVertexSize += vertexAdded.size();
				break;
			default:
				break;
		};
	}*/

	// record material changes for triangle faces:
	materialTriangleSegments.clear();
	if (materialTriangleList.size())
	{
		size_t m = 0;
		int last = materialTriangleList[m];
		materialTriangleSegments.push_back(m);
		while (++m < materialTriangleList.size())
		{
			if (materialTriangleList[m] != last)
			{
				last = materialTriangleList[m];
				materialTriangleSegments.push_back(m);
			}
		}
		materialTriangleSegments.push_back(materialTriangleList.size());
	}

	// record material changes for quad faces:
	materialQuadSegments.clear();
	if (materialQuadList.size())
	{
		size_t m = 0;
		int last = materialQuadList[m];
		materialQuadSegments.push_back(m);
		while (++m < materialQuadList.size())
		{
			if (materialQuadList[m] != last)
			{
				last = materialQuadList[m];
				materialQuadSegments.push_back(m);
			}
		}
		materialQuadSegments.push_back(materialQuadList.size());
	}
}